/**
 * Copyright (c) 2023 murenchao
 * taomu is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *       http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */
package cool.taomu.toolkit.jersey.jetty.provider

import com.google.inject.Inject
import com.google.inject.Injector
import com.google.inject.name.Named
import com.google.inject.servlet.GuiceFilter
import com.google.inject.servlet.GuiceServletContextListener
import cool.taomu.toolkit.Context
import cool.taomu.toolkit.jersey.GuiceResourceConfig
import java.nio.file.Paths
import java.util.Collections
import java.util.EnumSet
import javax.inject.Provider
import javax.servlet.DispatcherType
import org.eclipse.jetty.server.Handler
import org.eclipse.jetty.server.HttpConfiguration
import org.eclipse.jetty.server.HttpConnectionFactory
import org.eclipse.jetty.server.Server
import org.eclipse.jetty.server.ServerConnector
import org.eclipse.jetty.server.handler.ContextHandler
import org.eclipse.jetty.server.handler.HandlerCollection
import org.eclipse.jetty.server.handler.HandlerList
import org.eclipse.jetty.server.handler.HandlerWrapper
import org.eclipse.jetty.server.handler.ResourceHandler
import org.eclipse.jetty.servlet.ServletContextHandler
import org.eclipse.jetty.servlet.ServletHolder
import org.eclipse.jetty.webapp.WebAppContext
import org.eclipse.jetty.websocket.server.WebSocketHandler
import org.glassfish.jersey.servlet.ServletContainer
import org.slf4j.LoggerFactory

/**
 * https://cloud.tencent.com/developer/information/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8jetty%E5%AE%9E%E7%8E%B0Websocket-video
 */
class JettyProvider implements Provider<Server> {
	val static LOG = LoggerFactory.getLogger(JettyProvider);

	@Inject(optional=true)
	@Named("resourceConfig")
	String resourceConfig = GuiceResourceConfig.name;

	@Inject(optional=true)
	@Named("taomu.http.name")
	String name;

	@Inject(optional=true)
	@Named("taomu.http.host")
	String host = "0.0.0.0";

	@Inject(optional=true)
	@Named("taomu.http.port")
	int port = 8080;

	@Inject(optional=true)
	@Named("taomu.http.path")
	String contextPath = "/";

	@Inject(optional=true)
	@Named("taomu.http.staticPath")
	String staticBasePath = "./static";

	@Inject(optional=true)
	@Named("taomu.http.pathSpec")
	String pathSpec = "";

	HttpConfiguration httpConfiguration = new HttpConfiguration();

	Server server;

	@Inject
	Context supplier;

	@Inject(optional=true)
	WebSocketHandler webSocketkHandler;

	override get() {
		if (!contextPath.startsWith("/")) {
			contextPath = "/" + contextPath;
		}
		if (name === null || name.equals("")) {
			name = String.format("%s-%s", host, port);
		}
		server = new Server();
		var ServerConnector connector = new ServerConnector(server);
		connector.setName(name);
		connector.setHost(host);
		connector.setPort(port);
		connector.connectionFactories = Collections.singleton(new HttpConnectionFactory(httpConfiguration));
		server.addConnector(connector);

		var webAppContext = new WebAppContext();
		webAppContext.setServer(server);
		webAppContext.addFilter(GuiceFilter, #[this.pathSpec, "/*"].join(""), EnumSet.allOf(DispatcherType));
		var ServletHolder holder = new ServletHolder(ServletContainer);
		holder.setInitParameter("javax.ws.rs.Application", resourceConfig);

		webAppContext.addServlet(holder, #[this.pathSpec, "/*"].join(""));
		webAppContext.setResourceBase("/");
		webAppContext.setContextPath(contextPath);
		webAppContext.addEventListener(new GuiceServletContextListener() {
			override protected Injector getInjector() {
				return supplier.injectorSupplier.get();
			}
		});
		LOG.info("staticBasePath:{}",staticBasePath)
		var String staticDir = Paths.get(staticBasePath).toAbsolutePath().toString();
		/*var defaultServlet = new ServletHolder("default", DefaultServlet);
		 * //var staticDir = #[System.getProperty("user.dir"), StringUtils.defaultIfBlank(staticBasePath, "static")].
		 * //	join("/")
		 * LOG.info("web static file directory : {}", staticDir);
		 * defaultServlet.setInitParameter("resourceBase",staticDir);
		 * defaultServlet.setInitParameter("pathInfoOnly", "true");
		 webAppContext.addServlet(defaultServlet, "/static/*");	*/
		var ContextHandler rcontextHandler = new ContextHandler(contextPath);
		rcontextHandler.setResourceBase(staticDir);

		var ResourceHandler resourceHandler = new ResourceHandler();
		rcontextHandler.setHandler(resourceHandler);

		var HandlerList handlers = new HandlerList();
		if (webSocketkHandler !== null) {
			var ServletContextHandler contextHandler = new ServletContextHandler();
			contextHandler.setContextPath("/ws");
			contextHandler.handler = webSocketkHandler
			handlers.addHandler(contextHandler);
			handlers.addHandler(rcontextHandler);
			handlers.addHandler(webAppContext);
			setHandler(server, handlers);
		} else {
			handlers.addHandler(rcontextHandler);
			handlers.addHandler(webAppContext);
			setHandler(server, handlers);
		}

		server;
	}

	static def void setHandler(HandlerWrapper handlerWrapper, Handler handlerToAdd) {
		var Handler currentInnerHandler = handlerWrapper.getHandler();
		if (currentInnerHandler === null) {
			handlerWrapper.setHandler(handlerToAdd);
		} else if (currentInnerHandler instanceof HandlerCollection) {
			(currentInnerHandler as HandlerCollection).addHandler(handlerToAdd);
		} else if (currentInnerHandler instanceof HandlerWrapper) {
			setHandler(currentInnerHandler as HandlerWrapper, handlerToAdd);
		} else {
			var HandlerList handlerList = new HandlerList();
			handlerList.addHandler(currentInnerHandler);
			handlerList.addHandler(handlerToAdd);
			handlerWrapper.insertHandler(handlerWrapper);
		}
	}

}
